/* * * * M A P V I S I O N   L I C E N S E  *****************************************************************
 *
 * This MapVision software is © Filip Zavadil 2013. It is licensed under the LGPL license.
 * This MapVision software dynamically links to unmodified Qt5 Library.
 *
 * The Qt5 Library is © 2013 Qt® and/or its subsidiary(-ies), and is licensed under the
 * GNU Lesser General Public License version 2.1 with Qt® LGPL exception version 1.1.
 * Qt5 library is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License, version 2.1, as published by the Free Software Foundation.
 * Qt5 library is provided “AS IS”, without WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * As an additional permission to the GNU Lesser General Public License version 2.1, the object
 * code form of a “work that uses the Library” may incorporate material from a header file that is
 * part of the Library. You may distribute such object code under terms of your choice,
 * provided that: (i) the header files of the Library have not been modified; and (ii)
 * the incorporated material is limited to numerical parameters, data structure layouts, accessors,
 * macros, inline functions and templates; and (iii) you comply with the terms of Section 6 of the
 * GNU Lesser General Public License version 2.1.
 *
 * Moreover, you may apply this exception to a modified version of the Library, provided that such
 * modification does not involve copying material from the Library into the modified Library’s header
 * files unless such material is limited to (i) numerical parameters; (ii) data structure layouts;
 * (iii) accessors; and (iv) small macros, templates and inline functions of five lines or less in length.
 *
 * Furthermore, you are not required to apply this additional permission to a modified version of the Library.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this package;
 * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

 * The source code for Qt 5.X SDK is available from Qt Project here:
 * http://download.qt-project.org/official_releases/qtcreator/2.7/2.7.0/qt-creator-2.7.0-src.zip.
 *
 * ****************************** M A P V I S I O N    S O F T W A R E  ************************************
 *
 * Author:       Filip Zavadil
 * Description:  Software for displaying and exporting map tiles.
 * The sources and executables of MapVision software are available:
 *    - from project hosting on CodePlex.com :   http://mapvision.codeplex.com
 *    - from GIT repositories on Gitorious.org:  https://gitorious.org/mapvision
 * Visit MapVision fanpage on Facebook.com:      https://www.facebook.com/pages/Mapvision/631827753513935
 *
 * *********************************************************************************************************
*/

#include "mapview.h"
#include "zavaproj.h"
#include <QDebug>
#include <QApplication>
#include <QDesktopWidget>



MapView::MapView(QWidget *parent): QGraphicsView(parent)
{

    this->setCacheMode(QGraphicsView::CacheNone);
    this->setRenderHints(QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform );


    //QGraphicsView::setGraphicsEffect();
    connect(this,SIGNAL(zoomChanged(int)),this,SLOT(zoomHasChanged(int)));

    panEnabled = false;

    // initializing map scene
    scene = new MapScene();
    scene->setSceneRect(-30000,-30000,pow(10,100),pow(10,100));
    this->setScene(scene);

    lastCursor = Qt::OpenHandCursor;
    this->viewport()->setCursor(lastCursor);
    mapViewMode = PAN;

    selection = new SelectionRubberBand(this);


    // no scroll bars
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);


    // this will be ovewrited by setCenterLonLat() and setZoom();
    currentCenterPoint=QPoint(0,0);
    zoom=1;

    this->setFocus();

    centerOn(currentCenterPoint);

    welcome = new Welcome(this);

}

void MapView::setCenterLonLat(QPointF lonLat)
{
    this->setCenterPixels(zavaProj.lonlat2pixels(lonLat,zoom));
    this->update();
}

void MapView::setCenterLonLat(QString dmsFormat)
{
    QPointF lonlat = zavaProj.dms2lonlat(dmsFormat);
    this->setCenterLonLat(lonlat);
}

QPointF MapView::getCenterLonLat()
{
    return zavaProj.pixels2lonlat(this->getCenterPixels().toPoint(),zoom);
}

void MapView::setZoom(int zoom_)
{
    if (zoom_>=0 && zoom_<21){

        QPointF center=this->getCenterLonLat();
        zoom=zoom_;
        emit zoomChanged(zoom_);
        this->setCenterLonLat(center);
    }
}

void MapView::setViewScale(int viewScale)
{
    double scale = double(viewScale)/100.0;
   // QTransform matrix=this->transform();

    this->setTransform(QTransform(scale,0,0,0,scale,0,0,0,1));
    emit viewScaleChanged(int(this->transform().m11()*100));
    this->update();

}

QString MapView::getMapScale()
{
    //Exact length of the equator (according to wikipedia) is 40075.016686 km in WGS-84.
    //A horizontal tile size at zoom 0 would be 156543.034 meters.
    //Which gives us a formula to calculate resolution at any given zoom
    QString stringScale;
    double resolution = 156543.034 * cos( this->getCenterLonLat().y() * M_PI/180) / (pow(2,zoom) );

    int dpi = QApplication::desktop()->physicalDpiX();


    double scale = (dpi * 39.37 * resolution);
    stringScale =QString::number(scale,'f',0);

    if (stringScale.length() > 8){

        stringScale=stringScale.left(stringScale.count()-6) + tr(" mil.");
    }
    else if (stringScale.length() > 7){

        stringScale=stringScale.left(stringScale.count()-5);
        stringScale.insert(stringScale.length()-1,tr("."));

        stringScale += " " + tr("mil.");
    }
    else if (stringScale.length() > 6){

        stringScale=stringScale.left(stringScale.count()-4);
        stringScale.insert(stringScale.length()-2,tr("."));
        stringScale += " " + tr("mil.");
    }
    else if (stringScale.length() > 5){

        stringScale=stringScale.left(stringScale.count()-3);
        stringScale += " " + tr("tis.");
    }
    else if (stringScale.length() > 4){

        stringScale.insert(stringScale.length()-3,tr(" "));
    }
    else if (stringScale.length() > 3){

        stringScale.insert(stringScale.length()-3,tr(" "));
    }

    stringScale ="1 : "+stringScale;
    return stringScale;
}

void MapView::refresh()
{
    this->redraw();
}

void MapView::wheelEvent(QWheelEvent *event)
{
    if (!panEnabled) return;

    QPointF posCenter=getCenterPixels();
    QPointF posScene=this->mapToScene(event->pos());
    QPointF delta = posCenter-posScene;

    if(event->delta() > 0){
        //Zoom in
        if (zoom<20){
            ++zoom;
            this->setCenterPixels(posCenter*2-delta);
        }
    } else {
        //Zooming out
        if (zoom>=1){
            --zoom;
            this->setCenterPixels(posCenter/2+delta/2);
        }
    }
    emit zoomChanged(zoom);
}


void MapView::setCenterPixels(const QPointF centerPoint)
{
    currentCenterPoint = centerPoint;
    this->centerOn(currentCenterPoint);
}

void MapView::scaleView(qreal scaleFactor)
{
    scale(scaleFactor, scaleFactor);
    emit viewScaleChanged(int(this->transform().m11()*100));
    this->update();
}

void MapView::update()
{
    QPoint topLeft=this->mapToScene(this->rect().topLeft()).toPoint();
    QPoint bottomRight=this->mapToScene(this->rect().bottomRight()).toPoint();

    QRect sceneRect(topLeft,bottomRight);

    topLeft = zavaProj.pixels2tileFormat(sceneRect.topLeft(),zoom,ZavaProj::BOTTOM);
    bottomRight = zavaProj.pixels2tileFormat(sceneRect.bottomRight(),zoom,ZavaProj::BOTTOM);

    if (topLeft.x() >= 0 && topLeft.y() >= 0 && bottomRight.x() >= 0 && bottomRight.y() >= 0 ){
        QRect rectCurrent(topLeft,bottomRight);
        emit mapRectChanged(rectCurrent,zoom);
    }

    emit mapUpdated( this->getCenterLonLat(),zoom,this->getViewScale() );

}

void MapView::redraw()
{
    emit sceneCleared();
    this->update();
}

QRectF MapView::getRectLonlat()
{
    QPointF topLeft = zavaProj.pixels2lonlat(this->mapToScene(this->rect().topLeft()).toPoint(),zoom);
    QPointF bottomRight = zavaProj.pixels2lonlat(this->mapToScene(this->rect().bottomRight()).toPoint(),zoom);

    return QRectF(topLeft,bottomRight);
}


void MapView::mousePressEvent(QMouseEvent *event)
{
    QGraphicsView::mousePressEvent(event);

    lastPanPoint = event -> pos();

    emit clickOnLonlat(zavaProj.pixels2lonlat(this->mapToScene(event->pos()).toPoint(),zoom));
    emit mousePressed(event);
    this->invalidateScene();

}


void MapView::mouseMoveEvent(QMouseEvent *event)
{
    if (panEnabled ){
        if(!lastPanPoint.isNull()){
            this->viewport()->setCursor(Qt::ClosedHandCursor);
            //get how much we panned
            QPointF delta = this->mapToScene(lastPanPoint.toPoint()) - this->mapToScene(event -> pos());
            lastPanPoint = event -> pos();


            //Update the center ie. do the pan
            setCenterPixels(getCenterPixels() + delta);

            emit centerPointChanged(this->getCenterLonLat());
            this->update();
        }
    }

    // emits signal with lonlat coordinate
    emit mouseOnLonlat(zavaProj.lonlat2dms(zavaProj.pixels2lonlat(this->mapToScene(event->pos()).toPoint(),zoom)));

    emit mouseMoved(event);

    QGraphicsView::mouseMoveEvent(event);

}

void MapView::mouseReleaseEvent(QMouseEvent *event)
{

    //this->update();
    // redefining lastPanPoint
    lastPanPoint = QPoint();
    if (panEnabled) this->viewport()->setCursor(Qt::OpenHandCursor);

    // there can be implementation of kinematic (smooth)panning
    // with Qt Animation framewrok ?
    // not necessary

    // flickcharm.cpp

    emit mouseOnLonlat(zavaProj.lonlat2dms(zavaProj.pixels2lonlat(this->mapToScene(event->pos()).toPoint(),zoom)));

    QGraphicsView::mouseReleaseEvent(event);
    QWidget::mouseReleaseEvent(event);
    emit mouseReleased(event);
}

void MapView::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (panEnabled){


        QPointF posCenter=getCenterPixels();
        QPointF posScene=this->mapToScene(event->pos());
        QPointF delta = posCenter-posScene;
        if (event->button()==Qt::LeftButton){
            if (zoom<20) zoom++;
            emit zoomChanged(zoom);
            this->setCenterPixels(posCenter*2-delta);
        } else if (event->button()==Qt::RightButton){
            if (zoom>=1) zoom--;
            emit zoomChanged(zoom);
            this->setCenterPixels(posCenter/2+delta);
        }

        this->update();
    }

    QGraphicsView::mouseDoubleClickEvent(event);
}
bool MapView::viewportEvent(QEvent *event)
{

    QGraphicsView::viewportEvent(event);

    if (event->type()==QEvent::CursorChange){

    }

    return true;
}

void MapView::scrollContentsBy(int dx, int dy)
{
    QGraphicsView::scrollContentsBy(dx,dy);
}

void MapView::keyPressEvent(QKeyEvent *event)
{

    switch (event->key()) {
    case Qt::Key_Up:
        ;
        break;
    case Qt::Key_Down:

        break;
    case Qt::Key_Left:
        break;
    case Qt::Key_Right:
        ;
        break;
    case Qt::Key_Plus:
        this->scaleView(qreal(1.2));
        break;
    case Qt::Key_Minus:
        this->scaleView(1 / qreal(1.2));
        break;
    case Qt::Key_Space:
        if (!panEnabled && !event->isAutoRepeat()){
            panEnabled = true;
            this->viewport()->setCursor(Qt::OpenHandCursor);
        }
        break;
    case Qt::Key_Enter:

        break;
    case Qt::Key_U:
        this->update();
        break;
    case Qt::Key_I:
        break;
    case  Qt::Key_Control:
        ;
        break;
    default:
        ;
    }
    QGraphicsView::keyPressEvent(event);
}

void MapView::keyReleaseEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_Space:
        if (panEnabled && !event->isAutoRepeat() && lastCursor.shape()!=Qt::OpenHandCursor){
            this->viewport()->setCursor(lastCursor);
            panEnabled = false;
        }
        break;
    default:
        QGraphicsView::keyReleaseEvent(event);
    }

}

void MapView::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    this->setCenterPixels(currentCenterPoint);
    this->update();
}

void MapView::tilesVisible(QString layerName, bool isVisible)
{

    for (int i=0;i<scene->items().size();i++){
        if (scene->items()[i]->data(1)==layerName){
            scene->items()[i]->setVisible(isVisible);
        }
    }
}


void MapView::zoomHasChanged(int zoom_)
{
    if (panEnabled)  if (zoom_ >= 0 && zoom_ <21){
        this->redraw();
    }
}

void MapView::itemWasSelected(QGraphicsItem * item)
{
    emit itemSelected(item);
}

